<?php
/**
 * Created by PhpStorm.
 * User: holobo
 * Date: 2018/11/19
 * Time: 10:48
 */

namespace apps\marriage\core\service;


use apps\marriage\core\model\PayTrade;
use apps\marriage\core\payment\PaymentBase;
use think\Exception;
use think\facade\Log;
use vm\com\BaseService;
use vm\org\lock\DisLockFactory;

class PayTradeService extends BaseService
{
    protected $cachePrefix = 'pay_trade:';

    /**
     * @var SiteConfigService
     */
    private $siteConfigService;

    protected function init()
    {
        parent::init();
        $this->siteConfigService = service('SiteConfig', SERVICE_NAMESPACE);
    }

    /**
     * @return PayTrade
     */
    protected function getModel()
    {
        return new PayTrade();
    }

    public function create(array $data)
    {
        $data['trade_no'] = $this->genTradeNo();
        $data['pay_status'] = PayTrade::STATUS_WAIT_PAY;
        $data['dateline'] = time();
        $id = parent::create($data);
        if($id) {
            $orderService = service($data['order_type'], SERVICE_NAMESPACE);
            $orderService->onPayTradeCreated($data['order_id'], $data['trade_no']);
            return $data['trade_no'];
        }
        throw new Exception('创建失败');
    }

    private function genTradeNo()
    {
        do {
            $tradeNo = date('YmdHis') . rand_string(6, 1);
            $isExists = $this->info([
                'trade_no' => $tradeNo
            ]);
        } while($isExists);
        return $tradeNo;
    }

    public function getByTradeNo($tradeNo)
    {
        if($trade = cache($this->cachePrefix . $tradeNo)) {
            $trade['pay_status_label'] = PayTrade::getStatusLabel($trade['pay_status']);
            return $trade;
        }
        $trade = $this->info([
            'trade_no' => $tradeNo
        ]);
        if($trade) {
            cache($this->cachePrefix, $tradeNo, 3600*24);
            $trade['pay_status_label'] = PayTrade::getStatusLabel($trade['pay_status']);
        }
        return $trade;
    }

    public function query($tradeNo)
    {
        $trade = $this->getByTradeNo($tradeNo);
        $payment = 'apps\marriage\core\payment\\' . $trade['payment_code'];
        $cfg = [];
        if($trade['payment_code'] == PaymentBase::PAYMENT_CODE_WXPAY) {
            $cfg = [
                'app_id' => $this->siteConfigService->getValueByCode('weixin_mp_app_id'),
                'app_secret' => $this->siteConfigService->getValueByCode('weixin_mp_app_secret'),
                'mch_id' => $this->siteConfigService->getValueByCode('weixin_mp_mch_id'),
                'pay_key' => $this->siteConfigService->getValueByCode('weixin_mp_pay_sign_key'),
            ];
        }
        $payment = new $payment($cfg);
        $result = $payment->query($tradeNo);
        if($result && $result['pay_status'] == PaymentBase::PAY_STATUS_SUCCESS
            && $result['total_fee'] == $trade['order_amount']) {
            $this->paySuccess($tradeNo, $result['transaction_id'], $result['pay_time'], $result['attach']);
            return $result;
        }
        return null;
    }

    /**
     * 支付成功后回调
     * @param $tradeNo
     * @param $payTime
     * @return bool
     * @throws Exception
     */
    public function paySuccess($tradeNo, $transactionId, $payTime, $attach = '')
    {
        usleep(500000);
        // 开始加锁
        $lockKey = 'pay_trade_success:' . $tradeNo;
        $lock = DisLockFactory::instance()->lock($lockKey, 2000);
        if(!$lock) {
//            throw new Exception('发生错误');
            return false;
        }
        $result = $this->getModel()->paySuccess($tradeNo, $transactionId, $payTime);
        if($result) {
            try {
                $trade = $this->getByTradeNo($tradeNo);

                $orderService = service($trade['order_type'], SERVICE_NAMESPACE);
                $orderService->onPaySuccess($tradeNo, $transactionId, $payTime, $trade['payment_name']);

                cache($this->cachePrefix . $tradeNo, null);
                // 释放锁
                DisLockFactory::instance()->unlock($lockKey);
                return true;
            } catch (Exception $e) {
                Log::error(json_encode($e));
                // 释放锁
                DisLockFactory::instance()->unlock($lockKey);
                throw new Exception($e->getMessage());
            }
        }
        // 释放锁
        DisLockFactory::instance()->unlock($lockKey);
//        throw new Exception('发生错误');
        return false;
    }
}